/**
 * JEditor2
 * 
 */
(function() {
    "use strict";

    if(typeof(console) == "null" || typeof(console) == "undefined") {
        console = {log: function(s) {}, info: function(s) {}};
    }

    var namespace = function(name, scope) {
        var a = name.split(".");
        var p = (scope || window);

        for(var i = 0; i < a.length; i++) {
            var n = a[i];

            if(typeof(p[n]) == "undefined") {
                p[n] = {};
            }
            p = p[n];
        }
        return p;
    };

    namespace("com.skin.util");
    namespace("com.skin.upload");
    com.skin.util.namespace = namespace;
})();

(function() {
    "use strict";

    var KeyCode = com.skin.util.KeyCode = {
        "ESC":       27,
        "TAB":       9,
        "CTRL":      17,
        "SHIFT":     16,
        "SPACE":     32,
        "ENTER":     13,
        "BACKSPACE": 8,
        "SCROLL":    145,
        "CAPSLOCK":  20,
        "NUMLOCK":   144,
        "PAUSE":     19,
        "INSERT":    45,
        "HOME":      36,
        "DELETE":    46,
        "END":       35,
        "PAGEUP":    33,
        "PAGEDOWN":  34,
        "LEFT":      37,
        "UP":        38,
        "RIGHT":     39,
        "DOWN":      40,
        "F1":        112,
        "F2":        113,
        "F3":        114,
        "F4":        115,
        "F5":        116,
        "F6":        117,
        "F7":        118,
        "F8":        119,
        "F9":        120,
        "F10":       121,
        "F11":       122,
        "F12":       123
    };

    var e = com.skin.util.EventUtil = {};

    e.bind = function(object, handler) {
        return function(event) {
            return handler.call(object, (event || window.event));
        };
    };

    e.fire = function(element, type, bubbles, cancelable) {
        if(document.createEvent) {
            var event = document.createEvent("Event");
            event.initEvent(type, bubbles !== undefined ? bubbles : true, cancelable !== undefined ? cancelable : false);
            element.dispatchEvent(event);
        }
        else if(document.createEventObject) {
            var event = document.createEventObject();
            element.fireEvent("on" + type, event);
        }
        else if(typeof(element["on" + type]) == "function") {
            element["on" + type]();
        }
    };

    e.addListener = function(target, type, handler) {
        var a = type.split(",");

        for(var i = 0; i < a.length; i++) {
            if(target.attachEvent) {
                target.attachEvent("on" + a[i], handler);
            }
            else if(target.addEventListener) {
                target.addEventListener(a[i], handler, false);
            }
            else {
                target["on" + a[i]] = handler;
            }
        }
    };

    e.removeListener = function(target, type, handler) {
        if(target.detachEvent) {
            target.detachEvent("on" + type, handler);
        }
        else if(target.removeEventListener) {
            target.removeEventListener(type, handler, false);
        }
        else {
            target["on" + type] = null;
        }
    };

    e.cancel = function(event) {
        if(event == null) {
            return false;
        }

        if(event.stop) {
            event.stop();
        }
        else if(event.stopPropagation()) {
            event.stopPropagation();
        }
        else {
            event.cancelBubble = true;
        }

        if(event.preventDefault) {
            event.preventDefault();
        }

        event.cancel = true;
        event.returnValue = false;
        return false;
    };
})();

(function() {
    var StyleUtil = com.skin.util.StyleUtil = {};

    StyleUtil.getTop = function(e) {
        if(e.getBoundingClientRect != null) {
            return e.getBoundingClientRect().top + this.getDocumentScrollTop(e.ownerDocument);
        }

        var current = e;
        var top = e.offsetTop;

        while((current = current.offsetParent) !== null){
            top += current.offsetTop;
        }
        return top;
    };

    StyleUtil.getLeft = function(e) {
        if(e.getBoundingClientRect != null) {
            return e.getBoundingClientRect().left + this.getDocumentScrollLeft(e.ownerDocument);
        }

        var current = e;
        var left = e.offsetLeft;

        while((current = current.offsetParent) !== null) {
            left += current.offsetLeft;
        }
        return left;
    };

    StyleUtil.width = function(e) {
        var result = this.getStyle(e, "width");

        if(result != null) {
            result = parseInt(result.replace("px", ""));
        }
        return isNaN(result) ? 0 : result;
    };

    StyleUtil.height = function(e) {
        var result = this.getStyle(e, "height");

        if(result != null) {
            result = parseInt(result.replace("px", ""));
        }
        return isNaN(result) ? 0 : result;
    };

    StyleUtil.getDocumentScrollTop = function(doc) {
        var scrollTop = doc.body.scrollTop;

        if(scrollTop == 0) {
            return doc.documentElement.scrollTop;
        }
        return scrollTop;
    };

    StyleUtil.getDocumentScrollLeft = function(doc) {
        var scrollTop = doc.body.scrollLeft;

        if(scrollTop == 0) {
            return doc.documentElement.scrollLeft;
        }
        return scrollTop;
    };

    StyleUtil.getStyle = function(e, name) {
        if(e.style[name]) {
            return e.style[name];
        }
        else if(document.defaultView != null && document.defaultView.getComputedStyle != null) {
            var computedStyle = document.defaultView.getComputedStyle(e, null);

            if(computedStyle != null) {
                var property = name.replace(/([A-Z])/g, "-$1").toLowerCase();
                return computedStyle.getPropertyValue(property);
            }
        }
        else if(e.currentStyle != null) {
            return e.currentStyle[name];
        }
        return null;
    };

    StyleUtil.hasClass = function(src, name) {
        if(src == null || src.className == null) {
            return false;
        }

        var a = src.className.split(/\s+/);

        for(var i = 0; i < a.length; i++) {
            if(a[i] == name) {
                return true;
            }
        }
        return false;
    };

    StyleUtil.addClass = function(src, name) {
        if(src != null) {
            this.removeClass(src, name);

            if(src.className != null && src.className != "") {
                src.className = src.className + " " + name;
            }
            else {
                src.className = name;
            }
        }
    };

    StyleUtil.removeClass = function(src, name) {
        if(src != null) {
            var className = src.className;

            if(className != null && className != "") {
                var b = [];
                var a = className.split(/\s+/);

                for(var i = 0; i < a.length; i++) {
                    var c = (a[i]).replace(/(^\s*)|(\s*$)/g, "");

                    if(c.length > 0 && c != name) {
                        b[b.length] = c;
                    }
                }
                src.className = b.join(" ");
            }
        }
    };

    var DOM = com.skin.util.DOM = {};
    DOM.trim = function(source) {return (source != null ? source.toString().replace(/(^\s*)|(\s*$)/g, "") : "");};

    DOM.prev = function(e) {
        if(e == null || e.parentNode == null) {
            return null;
        }

        var n = e;

        while((n = n.previousSibling) != null) {
            if(n.nodeType == 1) {
                return n;
            }
        }
        return n;
    };

    DOM.next = function(e) {
        if(e == null || e.parentNode == null) {
            return null;
        }

        var n = e;

        while((n = n.nextSibling) != null) {
            if(n.nodeType == 1) {
                return n;
            }
        }
        return n;
    };

    DOM.getTop = function(e) {
        if(e == null) {
            return null;
        }

        var p = e;

        while(true) {
            if(p.parentNode == null) {
                return p;
            }

            var nodeName = p.parentNode.nodeName.toLowerCase();

            if(nodeName == "body" || nodeName == "html") {
                return p;
            }

            p = p.parentNode;

            if(p == null) {
                break;
            }
        }
        return null;
    };

    DOM.getParent = function(e, name) {
        if(e == null) {
            return null;
        }

        var p = e;

        while(true) {
            var nodeName = p.nodeName.toLowerCase();

            if(nodeName == name) {
                return p;
            }

            p = p.parentNode;

            if(p == null) {
                break;
            }
        }
        return null;
    };

    DOM.isEmpty = function(node) {
        if(node == null) {
            return true;
        }

        var nodeName = node.nodeName.toLowerCase();

        if(nodeName == "p" || nodeName == "div") {
            /**
             * 说明这是一个有效的隐藏元素
             */
            if(node.style.display == "none") {
                return false;
            }

            /**
             * 如果不可见说明为空元素
             */
            var width = StyleUtil.width(node);
            var height = StyleUtil.height(node);
            return (width == 0 || height == 0);
        }
        else {
            return false;
        }
    };

    DOM.clear = function(e) {
        if(e == null) {
            return;
        }

        var temp = [];
        var list = e.childNodes;
        console.log("================= clear =================");

        for(var i = 0; i < list.length; i++) {
            var node = list[i];

            if(node.nodeType != 1) {
                continue;
            }

            if(this.isEmpty(node)) {
                temp[temp.length] = node;
            }
        }

        console.log("remove.size: " + temp.length);

        for(var i = 0; i < temp.length; i++) {
            var node = temp[i];
            var width = StyleUtil.width(node);
            var height = StyleUtil.height(node);

            console.log("remove [" + width + ", " + height + "]: " + node.outerHTML);
            node.parentNode.removeChild(node);
        }
    };

    DOM.moveup = function(doc, ele) {
        if(ele == null || ele.parentNode == null) {
            return null;
        }

        var e = ele;
        var nodeName = ele.nodeName.toLowerCase();

        if(nodeName == "html" || nodeName == "body") {
            return null;
        }

        if(e.parentNode.nodeName == "A" || e.parentNode.nodeName == "a") {
            e = e.parentNode;
            nodeName = e.nodeName.toLowerCase();
        }

        if(nodeName == "tr" || nodeName == "td") {
            e = DOM.getParent(e, "table");
        }
        else if(nodeName == "li" || nodeName == "dd") {
            e = e.parentNode;
        }

        var parent = e.parentNode;
        var parentName = parent.nodeName.toLowerCase();

        if(parentName == "html" || parentName == "body" || parentName == "tr" || parentName == "td" || parentName == "li" || parentName == "dd") {
            return ele;
        }

        var prev = e;
        var list = [e];
        var frg = doc.createDocumentFragment();

        while((prev = prev.previousSibling) != null) {
            list[list.length] = prev;
        }

        for(var i = list.length - 1; i > -1; i--) {
            parent.removeChild(list[i]);
            frg.appendChild(list[i]);
        }

        parent.parentNode.insertBefore(frg, parent);
        return ele;
    };

    DOM.movetop = function(doc, ele) {
        if(ele == null || ele.parentNode == null) {
            return null;
        }

        var e = ele;
        var nodeName = e.nodeName.toLowerCase();

        if(nodeName == "html" || nodeName == "body") {
            return null;
        }

        if(e.parentNode.nodeName == "A" || e.parentNode.nodeName == "a") {
            e = e.parentNode;
        }

        if(nodeName == "tr" || nodeName == "td") {
            e = DOM.getParent(e, "table");
        }
        else if(nodeName == "li" || nodeName == "dd") {
            e = e.parentNode;
        }

        var top = DOM.getTop(e);

        if(top == null || top == e) {
            return ele;
        }

        var parent = e.parentNode;
        var parentName = parent.nodeName.toLowerCase();

        if(parentName == "html" || parentName == "body" || parentName == "tr" || parentName == "td" || parentName == "li" || parentName == "dd") {
            return ele;
        }

        var prev = e;
        var list = [e];
        var frg = doc.createDocumentFragment();

        while((prev = prev.previousSibling) != null) {
            list[list.length] = prev;
        }

        for(var i = list.length - 1; i > -1; i--) {
            parent.removeChild(list[i]);
            frg.appendChild(list[i]);
        }

        /**
         * !Note: 如果该节点的父节点前面存在兄弟节点，该操作会导致顺序改变
         */
        doc.body.insertBefore(frg, top);
        return ele;
    };
})();

(function(e) {
    var e = com.skin.util.Html = {};

    e.selfclosed = {"area": 1, "base": 1, "basefont": 1, "br": 1, "col": 1, "frame": 1, "hr": 1, "img": 1, "input": 1, "isindex": 1, "link": 1, "meta": 1, "param": 1, "embed": 1};

    e.replacement = {
        "<": "&lt;",
        ">": "&gt;",
        "\"": "&quot;",
        " ": "&nbsp;",
        "\u00ae": "&reg;",
        "\u00a9": "&copy;"
    };

    e.replace = function(e) {return com.skin.util.Html.replacement[e];};

    e.encode = function(source, crlf) {
        if(crlf == null) {
            crlf = "<br/>";
        }
        return source.replace(new RegExp("[<>\"\\u00ae\\u00a9]", "g"), com.skin.util.Html.replace).replace(new RegExp("\\r?\\n", "g"), crlf);
    };

    e.getInnerHtml = function(element) {
        var b = [];
        var childNodes = element.childNodes;
        for(var i = 0, length = childNodes.length; i < length; i++) {
            b[b.length] = this.format(childNodes[i], 0);
        }
        return b.join("");
    };

    e.format = function(element, level) {
        var nodeName = element.nodeName.toLowerCase();
        if(nodeName == "link" || nodeName == "script") {
            return "";
        }

        var b = [];

        // TEXT
        if(element.nodeType == 3) {
            b[b.length] = com.skin.util.Html.encode(element.nodeValue, "");
            return b.join("");
        }

        // TEXT CDATA COMMENT
        if(element.nodeType == 4 || element.nodeType == 8) {
            b[b.length] = node.nodeValue;
            return b.join("");
        }

        if(element.nodeType != 1) {
            return "";
        }

        b[b.length] = "<";
        b[b.length] = nodeName;

        var attributes = element.attributes;
        for(var i = 0, length = attributes.length; i < length; i++) {
            if(attributes[i].value != null && attributes[i].value != "" && attributes[i].value != "null") {
                if(attributes[i].value != null) {
                    b[b.length] = " ";
                    b[b.length] = attributes[i].nodeName;
                    b[b.length] = "=\"";
                    b[b.length] = this.encode(attributes[i].value);
                    b[b.length] = "\"";
                }
            }
        }

        if(this.selfclosed[nodeName] != null) {
            b[b.length] = "/>";
            return b.join("");
        }

        b[b.length] = ">";
        var childNodes = element.childNodes;

        if(childNodes.length > 0) {
            if(childNodes.length == 1 && (childNodes[0].nodeType == 3 || childNodes[0].nodeType == 4 || childNodes[0].nodeType == 8)) {
                b[b.length] = com.skin.util.Html.encode(childNodes[0].nodeValue, "");
            }
            else {
                for(var i = 0, length = childNodes.length; i < length; i++) {
                    b[b.length] = this.format(childNodes[i], level + 1);
                }
            }
        }

        b[b.length] = "</";
        b[b.length] = nodeName;
        b[b.length] = ">";
        return b.join("");
    };

    e.getCursorPosition = function(doc, object) {
        var data = {text: "", start: 0, end: 0};
        try{object.focus();}catch(e) {}

        if(object.setSelectionRange) {
            data.start= object.selectionStart;
            data.end = object.selectionEnd;
            data.text = (data.start != data.end) ? object.value.substring(object.start, object.end): "";
        }
        else if(doc.selection) {
            var i;
            var range = doc.selection.createRange();
            var textRange = doc.body.createTextRange();
            textRange.moveToElementText(object);

            data.text = range.text;
            data.bookmark = range.getBookmark();

            // object.moveStart(sUnit [, iCount])
            // Return Value: Integer that returns the number of units moved.
            for(i = 0; textRange.compareEndPoints("StartToStart", range) < 0 && range.moveStart("character", -1) !== 0; i++) {
                // Why? You can alert(object.value.length)
                if(object.value.charAt(i) == "/n") {
                    i++;
                }
            }

            data.start = i;
            data.end = data.text.length + data.start;
        }

        object.setAttribute("cursorPosition", data.start + ":" + data.end);
        return data;
    };
})();

(function(e) {
    var e = com.skin.util.StringUtil = {};

    e.trim = function(source) {return (source != null ? source.toString().replace(/(^\s*)|(\s*$)/g, "") : "");};
    e.startsWith = function(source, prefix) {
        if(source.length >= prefix.length) {
            return (source.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase());
        }
        return false;
    };

    e.replace = function(source, context) {
        var c;
        var buffer = [];
        var map = (context || {});

        for(var i = 0, length = source.length; i < length; i++) {
            c = source.charAt(i);

            if(c == "$" && i < length - 1 && source.charAt(i + 1) == "{") {
                var k = source.indexOf("}", i + 2);

                if(k > -1) {
                    var name = source.substring(i + 2, k);
                    var value = map[name];

                    if(value != null) {
                        buffer[buffer.length] = value;
                    }
                    i = k;
                }
                else {
                    buffer[buffer.length] = source.substring(i + 2);
                    break;
                }
            }
            else {
                buffer[buffer.length] = c;
            }
        }
        return buffer.join("");
    };

    var I18N = com.skin.util.I18N = {};
    var MessageFormat = com.skin.util.MessageFormat = {};

    I18N.load = function(language) {
        if(language != null && language.length > 0) {
            var url = JEditor.getHome() + "/lang/" + language + ".js";
            com.skin.util.Loader(document, url);
        }
    };

    I18N.format = MessageFormat.format = function(pattern) {
        var c;
        var buffer = [];
        var args = arguments;

        /**
         * var pattern = "这是一个示例\\{\\}, 这是一个示例{1}, 这是一个示例{2}";
         */
        for(var i = 0, length = pattern.length; i < length; i++) {
            c = pattern.charAt(i);

            if(c == "\\" && i < length - 1) {
                i = i + 1;
                c = pattern.charAt(i);

                if(c == "{" || c == "}") {
                    buffer[buffer.length] = c;
                    continue;
                }
                else {
                    buffer[buffer.length] = "\\";
                    buffer[buffer.length] = c;
                    continue;
                }
            }
            else if(c == "{") {
                var k = pattern.indexOf("}", i + 1);

                if(k > -1) {
                    var index = parseInt(pattern.substring(i + 1, k));

                    if(!isNaN(index) && index > 0 && index < args.length) {
                        buffer[buffer.length] = (args[index] || "");
                    }
                    i = k;
                }
                else {
                    buffer[buffer.length] = pattern.substring(i + 1);
                    break;
                }
            }
            else {
                buffer[buffer.length] = c;
            }
        }
        return buffer.join("");
    };
})();

(function() {
    var e = com.skin.upload.FileUpload = function(settings) {
        this.settings = settings;
    };

    e.prototype = {
        open: function() {
            var self = this;
            var settings = this.settings;
            var inputId = "_fileupload_" + (new Date()).getTime();
            var input = this.getFileInput(inputId, inputId, settings.accept, settings.multiple);
            var onchange = function(event) {
                var e = (event || window.event);
                var input = e.target;
                var files = input.files;

                if(files == null || files.length < 1) {
                    input.parentNode.parentNode.removeNode(input.parentNode);
                    return;
                }
                self.select(input.files);
            };
            this.addEventListener(input, "change", onchange);
            input.click();
        },
        select: function(files) {
            if(this.settings.select != null) {
                this.settings.select.apply(this, [files]);
            }
        },
        upload: function(url, name, file, data) {
            var self = this;
            var settings = this.settings;
            var xmlHttpRequest = this.getXmlHttpRequest();

            if(xmlHttpRequest == null || xmlHttpRequest == undefined) {
                if(settings.error != null) {
                    settings.error(null);
                }
                return;
            }

            var headers = settings.headers;
            var data = this.getFormData(name, file, data);
            xmlHttpRequest.open("post", url, true);

            xmlHttpRequest.onreadystatechange = function() {
                if(xmlHttpRequest.readyState == 4) {
                    if(xmlHttpRequest.status == 0 || xmlHttpRequest.status == 200) {
                        if(settings.success != null) {
                            var result = self.getResponse(xmlHttpRequest.responseText, settings.dataType);
                            settings.success(result);
                        }
                    }
                    else {
                        if(settings.error != null) {
                            settings.error(xmlHttpRequest);
                        }
                    }
                }
            };

            xmlHttpRequest.ontimeout = settings.ontimeout;
            xmlHttpRequest.onprogress = settings.onprogress;
            xmlHttpRequest.upload.onprogress = settings.onuploadprogress;

            if(headers != null) {
                for(var i = 0; i < headers.length; i++) {
                    var header = headers[i];
                    xmlHttpRequest.setRequestHeader(header.name, header.value);
                }
            }
            xmlHttpRequest.send(data);
            return xmlHttpRequest;
        },
        addEventListener: function(target, type, handler) {
            if(target == null) {
                return;
            }

            if(target.attachEvent) {
                target.attachEvent("on" + type, handler);
            }
            else if(target.addEventListener) {
                target.addEventListener(type, handler, false);
            }
            else {
                target["on" + type] = handler;
            }
        },
        getFileInput: function(id, name, accept, multiple) {
            var params = {
                "id": id,
                "name": name,
                "type": "file",
                "accept": (accept || "*"),
                "multiple": multiple
            };

            var div = this.createElement("div");
            var input = this.createElement("input", params);
            div.style.display = "none";
            div.appendChild(input);
            document.body.appendChild(div);
            return input;
        },
        createElement: function(name, params) {
            var e = null;

            if(window.ActiveXObject) {
                e = document.createElement("<" + name + ">");
            }
            else {
                e = document.createElement(name);
            }

            if(params != null) {
                for(var i in params) {
                    if(params[i] != null) {
                        e.setAttribute(i, params[i]);
                    }
                }
            }
            return e;
        },
        getXmlHttpRequest: function() {
            var xmlHttpRequest = null;

            if(window.ActiveXObject != null) {
                xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
            }
            else {
                xmlHttpRequest = new XMLHttpRequest();
            }
            return xmlHttpRequest;
        },
        getFormData: function(name, file) {
            var args = arguments;
            var formData = new FormData();
            formData.append(name, file, file.name);

            for(var i = 2; i < args.length; i++) {
                this.append(formData, args[i]);
            }
            return formData;
        },
        append: function(formData, data) {
            if(formData == null || data == null) {
                return;
            }

            for(var i in data) {
                var value = data[i];
                var type = typeof(data[i]);

                if(type == "string" || type == "number" || type == "boolean") {
                    formData.append(i, value);
                }
                else if(className == "object" && value.length != null) {
                    for(var j = 0; j < value.length; j++) {
                        if(value[j] != null) {
                            formData.append(i, value[j]);
                        }
                    }
                }
                else {
                    formData.append(i, value);
                }
            }
        },
        getBlob: function(file, start, end) {
            var blob = file;
            start = (start || 0);
            end = (end || 0);

            if(file.slice) {
                console.log("file.slice: " + start + "/" + end);
                blob = file.slice(start, end);
                blob.name = file.name;
                blob.contentType = file.type;
            }

            if(file.webkitSlice) {
                console.log("file.webkitSlice " + start + "/" + end);
                blob = file.webkitSlice(start, end);
                blob.name = file.name;
                blob.contentType = file.type;
            }

            if(file.mozSlice) {
                console.log("file.mozSlice " + start + "/" + end);
                blob = file.mozSlice(start, end);
                blob.name = file.name;
                blob.contentType = file.type;
            }
            return blob;
        },
        getResponse: function(content, dataType) {
            try {
                if(dataType == "text") {
                    return content;
                }

                if(dataType == "script") {
                    var e = document.createElement("script");
                    e.type = "text/javascript";
                    e.text = content;
                    document.body.appendChild(e);
                    return content;
                }

                if(dataType == "json") {
                    return eval("(" + content + ")");
                }
                else {
                    /**
                     * text, html, etc..
                     */
                }
            }
            catch(e) {
            }
            return content;
        }
    };
})();

(function() {
    "use strict";
    var ID = com.skin.util.ID = {};

    ID.next = function() {
        if(this.seed == null) {
            this.seed = 0;
        }

        this.seed++;
        return this.seed;
    };

    var Loader = com.skin.util.Loader = {};

    Loader.css = function() {
        var doc = null;
        var url = null;

        if(arguments.length == 1) {
            doc = document;
            url = arguments[0];
        }
        else if(arguments.length > 1) {
            doc = arguments[0];
            url = arguments[1];
        }
        else {
            return;
        }

        var e = doc.createElement("link");
        e.rel = "stylesheet";
        e.type = "text/css";
        e.href = url;
        (doc.getElementsByTagName("head"))[0].appendChild(e);
    };

    Loader.script = function() {
        var doc = null;
        var url = null;

        if(arguments.length == 1) {
            doc = document;
            url = arguments[0];
        }
        else if(arguments.length > 1) {
            doc = arguments[0];
            url = arguments[1];
        }
        else {
            return;
        }

        var e = doc.createElement("script");
        e.type = "text/javascript";
        e.src = url;
        (doc.getElementsByTagName("head"))[0].appendChild(e);
    };

    var Browser = com.skin.util.Browser = {};
    var agent = window.navigator.userAgent.toLowerCase();
    Browser.ie = /(msie\s|trident.*rv:)([\w.]+)/.test(agent);
    Browser.webkit = (agent.indexOf("applewebkit/") > -1);
    Browser.opera = (window.opera == true && window.opera.version != null);
    Browser.gecko =(window.navigator.product == 'Gecko' && !Browser.webkit && !Browser.opera && !Browser.ie);

    window.JEditor = {};
    JEditor.version = "1.0.0";
    JEditor.tools   = [];
    JEditor.plugins = {};
    JEditor.context = {};
    JEditor.trim = function(source) {return (source != null ? source.toString().replace(/(^\s*)|(\s*$)/g, "") : "");};
    JEditor.getHome = function() {
        if(JEditor.home == null) {
            var home = null;
            var scripts = document.getElementsByTagName("script");

            for(var i = 0, length = scripts.length; i < length; i++) {
                var src = scripts[i].src;

                if(src != null && src != undefined && src.length > 0) {
                    if(src.match(/je[^\/]*\.js/i)) {
                        home = src.replace(/je[^\/]*\.js/, "");
                        break;
                    }
                }
            }

            if(home == null) {
                JEditor.home = "";
            }
            else {
                home = JEditor.trim(home);

                if(home.length >= 1) {
                    if(home.substring(home.length - 1) == "/") {
                        home = home.substring(0, home.length - 1);
                    }
                }
                JEditor.home = home;
            }
        }
        return JEditor.home;
    };

    JEditor.setInstance = function(instance) {
        JEditor.context[instance.instanceId] = instance;
    };

    JEditor.getInstance = function(src) {
        var id = null;

        if(src == null || src == "undefined") {
            return;
        }

        if(typeof(src) == "string") {
            id = src;
        }
        else {
            id = src.getAttribute("editorId");
        }
        return JEditor.context[id];
    };

    JEditor.load = function(url) {
        var e = document.createElement("script");
        e.type = "text/javascript";
        e.src = url;
        document.body.appendChild(e);
    };

    JEditor.plugin = function(plugin) {
        if(plugin != null) {
            var name = plugin.name;
            this.plugins[name] = plugin;
        }
    };

    JEditor.getPlugin = function(name) {
        return (JEditor.getPlugins())[name];
    };

    JEditor.getPlugins = function() {
        return this.plugins;
    };

    JEditor.put = function(bean, name, value) {
        var property = bean[name];

        if(property == null) {
            bean[name] = value;
        }
        else {
            var className = typeof(property);

            if(className == "string" || className == "number" || className == "boolean") {
                bean[name] = [property, value];
            }
            else if(className == "object" && property.length != null) {
                property[property.length] = value;
            }
        }
    };

    JEditor.wrap = function(bean, list) {
        var e = null;
        var m = null;
        var n = null;
        var t = null;

        for(var i = 0; i < list.length; i++) {
            e = list[i];
            n = e.nodeName.toLowerCase();
            m = e.getAttribute("name");
            t = e.getAttribute("type");

            if(m == null || m == "") {
                continue;
            }

            if(n == "input" && t == "text" || t == "hidden") {
                JEditor.put(bean, m, e.value);
            }
            else if((n == "radio" || n == "checkbox") && e.checked == true) {
                JEditor.put(bean, m, e.value);
            }
            else if(n == "select") {
                if(e.getAttribute("multiple") == null || e.getAttribute("multiple") == "") {
                    JEditor.put(bean, m, e.value);
                }
                else {
                    var opts = e.options;
                    for(var j = 0; j < opts.length; j++) {
                        if(opts[i].selected == true) {
                            JEditor.put(bean, opts[i].value);
                        }
                    }
                }
            }
        }
    };

    JEditor.upload = function() {
        var editor = JEditor.instance;

        if(editor != null && editor.uploader != null) {
            editor.uploader.open();
        }
    };

    JEditor.getSelect = function(doc, object) {
        if(object == null) {
            return;
        }

        try {
            object.focus();
        }
        catch(e) {
        }

        var selection = doc.selection;

        if(object.selectionStart != null) {
            var start = object.selectionStart + 0;
            var end = object.selectionEnd + 0;
            return object.value.substring(start, end);
        }
        else if(selection && selection.createRange) {
            return selection.createRange().text;
        }
        return "";
    };

    JEditor.insert = function(doc, object, text) {
        if(object == null || text == null) {
            return;
        }

        try {
            object.focus();
        }
        catch(e) {
        }

        var selection = doc.selection;

        if(selection && selection.createRange) {
            var range = null;
            var cursorPosition = object.getAttribute("cursorPosition");

            if(cursorPosition != null) {
                var start = (cursorPosition.split(":"))[0];
                var end = (cursorPosition.split(":"))[1];
                range = selection.createRange();
                range.moveToElementText(object);
                range.moveStart("character", parseInt(start));
                range.moveEnd("character", parseInt(end) - object.value.length);
                range.select();
            }
            else {
                range = selection.createRange();
            }

            range.text = text;

            var getLength = function(s) {
                var userAgent = window.navigator.userAgent.toLowerCase();
                var isOpera = userAgent.indexOf("opera") != -1 && opera.version();
                var isIE = (userAgent.indexOf("msie") != -1 && !isOpera) && userAgent.substr(userAgent.indexOf("msie") + 5, 3);
                var length = (isIE && s.indexOf("\n") != -1) ? s.replace(/\r?\n/g, "_").length : s.length;
                return length;
            };

            var length = 0 - getLength(text);
            range.moveStart("character", length);
        }
        else if(object.selectionStart != null) {
            var start = object.selectionStart + 0;
            object.value = object.value.substring(0, start) + text + object.value.substring(object.selectionEnd);
            object.selectionStart = start + text.length;
            object.selectionEnd = start + text.length;
        }
        else {
            object.value += text;
        }
    };

    JEditor.getSource = function(textarea) {
        var instance = null;

        if(textarea != null) {
            var id = textarea.getAttribute("editorId");

            if(id != null) {
                instance = JEditor.getInstance(id);
            }
        }
        if(instance != null) {
            return instance.getSource();
        }
        return null;
    };
})();

/**
 * JEditor.Editor
 */
(function() {
    "use strict";
    var ID = com.skin.util.ID;
    var DOM = com.skin.util.DOM;
    var EventUtil = com.skin.util.EventUtil;
    var StyleUtil = com.skin.util.StyleUtil;
    var StringUtil = com.skin.util.StringUtil;

    var Mark = function(dombarId, doc) {
        this.dombarId = dombarId;
        this.doc = doc;
    };

    Mark.prototype.getDom = function(e) {
        var p = e;
        var b = [];
        var n = [];

        while(true) {
            if(p == null) {
                break;
            }

            var nodeName = p.nodeName.toLowerCase();

            if(nodeName == "html" || nodeName == "body") {
                break;
            }

            n[n.length] = nodeName;
            p = p.parentNode;
        }

        for(var i = n.length - 1; i > -1; i--) {
            var name = n[i];
            b[b.length] = "<span class=\"je-txt\">&gt;</span>";
            b[b.length] = "<span class=\"je-tag\" level=\"" + i + "\">" + name + "</span>";
        }
        return b.join("");
    };

    Mark.prototype.getCurrent = function(e) {
        var p = e;

        while((p != null && p.nodeType != 1)) {
            p = p.parentNode;
        }
        return p;
    };

    Mark.prototype.getRange = function() {
        var win = (this.doc.defaultView || this.doc.parentWindow);

        if(win.getSelection) {
            var selection = win.getSelection();

            if(selection.rangeCount > 0) {
                return selection.getRangeAt(0);
            }
        }
        else if(this.doc.selection) {
            return this.doc.selection.createRange();
        }
        return null;
    };

    Mark.prototype.select = function() {
        var element = this.getElement();

        if(element == null) {
            return;
        }

        if(this.doc.createRange != null) {
            var range = this.doc.createRange();
            range.selectNode(element);
        }
    };

    /**
     * 重新显示当前元素
     */
    Mark.prototype.show = function() {
        this.mark(this.element);
    };

    /**
     * 隐藏当前元素
     */
    Mark.prototype.hide = function() {
        this.element = null;
        this.getFrame(this.doc).style.display = "none";
        document.getElementById(this.dombarId).innerHTML = "";
    };

    /**
     * 显示元素
     */
    Mark.prototype.mark = function(e) {
        if(e == null || e.parentNode == null) {
            this.hide();
            return null;
        }

        /**
         * self
         */
        if(e.className == "je-ctrl") {
            return null;
        }

        /**
         * text
         */
        if(e.nodeType == 3) {
            this.hide();
            return null;
        }

        var nodeName = e.nodeName.toLowerCase();

        if(nodeName == "body" || nodeName == "textarea" || nodeName == "html") {
            this.hide();
            return null;
        }

        this.element = e;
        document.getElementById(this.dombarId).innerHTML = this.getDom(e);

        var y = StyleUtil.getTop(e);
        var x = StyleUtil.getLeft(e);
        var w = e.offsetWidth;
        var h = e.offsetHeight;
        var frame = this.getFrame(this.doc);

        frame.style.top = y + "px";
        frame.style.left = x + "px";
        frame.style.width = w + "px";
        frame.style.height = h + "px";
        frame.style.display = "inline-block";
        return e;
    };

    Mark.prototype.getElement = function() {
        if(this.element == null) {
            return null;
        }

        if(this.element.parentNode == null) {
            this.hide();
            return null;
        }
        return this.element;
    };

    Mark.prototype.getTop = function() {
        return DOM.getTop(this.element);
    };

    Mark.prototype.getParent = function(name) {
        return DOM.getParent(this.element, name);
    };

    Mark.prototype.getFrame = function(doc) {
        var id = "je-node-frame";
        var frame = doc.getElementById(id);

        if(frame == null) {
            frame = doc.createElement("div");
            frame.id = id;
            frame.className = "je-ctrl je-frame";
            frame.style.cssText = "margin: 0px; padding: 0px; position: absolute; display: none;"
                   + " width: 0px; height: 0px; top: 0px; left: 0px;"
                   + " border: 0px dashed #3399ff; box-sizing: border-box; background-color: #0e89e6; opacity: 0.1; cursor: default; z-index: -1;";
            doc.body.appendChild(frame);
        }
        return frame;
    };

    JEditor.Editor = function(config) {
        this.id = ID.next();
        this.iframeId = "je_iframe" + this.id;
        this.instanceId = "je_container" + this.id;
        this.toolbarId = "je_toolbar" + this.id;
        this.dombarId = "je_dombar" + this.id;
        this.domLockId = "je_domlock" + this.id;

        this.mode = 1;
        this.shortcuts = {};
        this.i18n = JEditor.I18N;
        JEditor.setInstance(this);
    };

    var e = window.JEditor.Editor.prototype;

    e.create = function(options) {
        var a = (options.toolbar || ("save,cut,copy,paste,|,formatBlock,fontName,fontSize,|,bold,italic,underline,strikethrough,foreColor,"
                + "backColor,selectall,removeformat,|,align,list,dedent,indent,|,link,unlink,anchor,image,flash,media,hr,"
                + "emot,table,|,source,preview,print,fullscreen,about,formdesigner")).split(",");
        var m = (JEditor.tools || []);
        var t = (options.target != null ? document.getElementById(options.target) : null);

        if(t == null) {
            return;
        }

        var b = [];
        b[b.length] = "<div id=\"" + this.toolbarId + "\" class=\"je-toolbar\" unselectable=\"on\">";

        for(var i = 0; i < a.length; i++) {
            if(a[i] == "|") {
                b[b.length] = "<a class=\"je-button separator\" href=\"javascript:void(0)\" tabindex=\"-1\" unselectable=\"on\">|</a>";
            }
            else {
                var e = m[a[i]];

                if(e != null) {
                    var title = StringUtil.replace(e.title, this.i18n);

                    if(e.shortcut != null && e.shortcut != "") {
                        this.shortcuts[e.shortcut] = e.command;
                        title = title + " (" + e.shortcut + ")";
                    }

                    b[b.length] = "<a class=\"je-button " + a[i]+ " enabled\" href=\"javascript:void(0)\""
                        + "tabindex=\"-1\" unselectable=\"on\" command=\"" + e.name + "\" title=\"" + title + "\"></a>";
                }
            }
        }

        b[b.length] = "<div class=\"je-dombar-container\">";
        b[b.length] =     "<span id=\"" + this.domLockId + "\" class=\"checkbox\" title=\"右键锁定: 锁定之后点击右键菜单将不再自动定位到右键点击的元素。\"></span>"
        b[b.length] =     "<span class=\"je-txt\">html</span>";
        b[b.length] =     "<span class=\"je-txt\">&gt;</span>";
        b[b.length] =     "<span class=\"je-txt\">body</span>";
        b[b.length] =     "<div id=\"" + this.dombarId + "\" class=\"je-dombar\"></div>"
        b[b.length] = "</div></div>";
        b[b.length] = "<div>";
        b[b.length] =     "<iframe id=\"" + this.iframeId + "\" frameborder=\"0\" scrolling=\"yes\"";
        b[b.length] =     " style=\"margin: 0px auto; overflow-x: hidden; display: none;\" src=\"about:blank\"></iframe>";
        b[b.length] = "</div>";

        var c = document.createElement("div");
        c.id = this.instanceId;
        c.className = "je-container";
        c.innerHTML = b.join("");
        t.appendChild(c);

        var c = this.getContainer();
        var iframe = this.getIFrame();
        var doc = iframe.contentWindow.document;
        var theme = "";

        if(options.theme != null) {
            theme = "<link type=\"text/css\" rel=\"stylesheet\" href=\"" + options.theme + "\"/>";
        }

        doc.open("text/html; charset=utf-8");
        doc.write(["<!DOCTYPE html>",
            "<html xmlns=\"http://www.w3.org/1999/xhtml\">",
            "<head>",
            "<title>JEditor</title>",
            "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>",
            "<link type=\"text/css\" rel=\"stylesheet\" href=\"" + JEditor.getHome() + "/css/iframe.css\"/>",
            theme,
            "</head>",
            "<body class=\"je-editor\" contentEditable=\"true\" spellcheck=\"false\">加载中，请稍候...</body></html>"].join(""));
        doc.close();
        doc.designMode = "on";
        doc.body.innerHTML = (options.html || "");

        var self = this;

        setTimeout(function() {
            self.setup(JEditor.plugins);
            self.resize();
        }, 300);
        return this;
    };

    e.dispatch = function(command) {
        if(command == null) {
            return false;
        }

        var tool = JEditor.tools[command];

        if(tool == null) {
            return false;
        }

        if(tool.click != null) {
            var args = [this].concat([].slice.call(arguments, 1));
            tool.click.apply(tool, args);
            return true;
        }

        if(tool.execute != null) {
            var args = [this].concat([].slice.call(arguments, 1));
            tool.execute.apply(tool, args);
            return true;
        }
        return false;
    };

    /**
     * 安装插件
     */
    e.setup = function(plugins) {
        this.tryexec("useCSS", !1, !1);
        this.tryexec("styleWithCSS", !1, !1);
        this.tryexec("enableObjectResizing", !0, !0);
        this.tryexec("enableInlineTableEditing", !0, !0);
        this.tryexec("BackgroundImageCache", !0, !0);

        for(var name in plugins) {
            var plugin = plugins[name];
            console.info("plugin.name: [" + name + "]");

            if(plugin != null && plugin.execute != null) {
                plugin.execute(this);
            }
        }
    };

    e.getPlugin = function(command) {
        return (JEditor.getPlugins())[command];
    };

    e.getContainer = function() {
        return document.getElementById(this.instanceId);
    };

    e.getToolbar = function() {
        return document.getElementById(this.toolbarId);
    };

    e.getIFrame = function() {
        return document.getElementById(this.iframeId);
    };

    e.getWindow = function() {
        return this.getIFrame().contentWindow;
    };

    e.getDocument = function() {
        return this.getWindow().document;
    };

    e.getBody = function() {
        return this.getWindow().document.body;
    };

    e.getEditor = function() {
        return this.getWindow().document.body;
    };

    e.getDombar = function() {
        return document.getElementById(this.dombarId);
    };

    e.getTextarea = function() {
        return this.getDocument().getElementById("je-textarea");
    };

    e.getTool = function(name) {
        var list = this.getToolbar().childNodes;

        for(var i = 0, length = list.length; i < length; i++) {
            var node = list[i];

            if(node.nodeType == 1 && node.getAttribute("command") == name) {
                return node;
            }
        }
        return null;
    };

    e.getDomLock = function() {
        return (document.getElementById(this.domLockId).getAttribute("checked") == "1");
    };

    e.getMark = function() {
        if(this.mark == null) {
            this.mark = new Mark(this.dombarId, this.getDocument());
        }
        return this.mark;
    };

    e.focus = function() {
        if(this.mode == 1) {
            this.getWindow().focus();
        }
        else {
            this.getTextarea().focus();
        }
    };

    e.getOffsetTop = function(e) {
        var iframe = this.getIFrame();
        var doc = this.getDocument();
        var offsetTop = StyleUtil.getTop(e);
        var scrollTop = StyleUtil.getDocumentScrollTop(doc);
        return StyleUtil.getTop(iframe) + offsetTop - scrollTop;
    };

    e.getOffsetLeft = function(e) {
        var iframe = this.getIFrame();
        var doc = this.getDocument();
        var offsetLeft = StyleUtil.getLeft(e);
        var scrollLeft = StyleUtil.getDocumentScrollLeft(doc);
        return StyleUtil.getLeft(iframe) + offsetLeft - scrollLeft;
    };

    e.execute = function(command) {
        var plugin = this.getPlugin(command);

        if(plugin != null) {
            var args = Array.prototype.slice.call(arguments, 1);
            plugin.execute.apply(this, args);
        }
    },

    e.getSelection = function() {
        var win = this.getWindow();
        return (win.getSelection ? win.getSelection() : win.document.selection);
    };

    e.getRange = function() {
        var selection = this.getSelection();

        if(selection.createRange) {
            return selection.createRange();
        }
        else if(selection.createTextRange) {
            return selection.createTextRange();
        }
        return null;
    };

    e.getBookmark = function() {
        this.focus();
        var range = this.getRange();

        if(range != null) {
            range = (range.cloneRange ? range.cloneRange() : range);
            var w = this.getWindow();
            return (this.bookmark = {top: w.scrollTop, range: range});
        }
        return null;
    };

    e.setBookmark = function() {
        if(this.bookmark != null) {
            var range = this.bookmark.range;

            if(this.IE) {
                range.select();
            }
            else {
                var s = this.getSelection();
                s.removeAllRanges(),
                s.addRange(range);
            }
            this.getWindow().scrollTop = this.bookmark.top;
            this.bookmark = null;
        }
        else {
            this.focus();
        }
    };

    /**
     * this.i18n = {
     *     "table.info" = "{1}行 x {2}列"
     * }
     *
     * var message = getLang("table.info", 3, 3);
     * output: 3行 x 3列
     * @param key
     * @param args
     * @return String
     */
    e.getLang = function(/* String */ key /* Object ... args */) {
        var value = this.i18n[key];

        if(value == null || value == undefined) {
            return "";
        }

        var i18n = com.skin.util.I18N;
        var args = [value].concat([].slice.call(arguments, 1));
        return i18n.format.apply(i18n, args);
    };

    e.exec = function(c, t, a) {
        this.setBookmark();
        this.getDocument().execCommand(c, t, a);
    };

    e.tryexec = function(c, t, a) {
        try{
            this.exec(c, t, a);
            return !0;
        }
        catch(e) {
            return !1;
        }
    };

    e.pasteText = function(text) {
        this.pasteHtml(com.skin.util.Html.encode(text));
    };

    e.pasteHtml = function(html) {
        if(this.mode == 1) {
            this.setBookmark();
            if(this.IE) {
                this.getRange().pasteHTML(html);
            }
            else {
                this.exec("InsertHtml", !1, html);
            }
        }
        else {
            var code = html;
            var textarea = this.getTextarea();
            JEditor.insert(this.getDocument(), textarea, code);
        }
    };

    e.setSource = function(source) {
        this.getEditor().innerHTML = source;
    };

    e.getSource = function() {
        if(this.mode == 1) {
            return this.getEditor().innerHTML;
        }
        else {
            return this.getTextarea().value;
        }
    };

    e.toggle = function() {
        if(this.mode == 1) {
            this.setMode(2);
        }
        else {
            this.setMode(1);
        }
    };

    e.setMode = function(mode) {
        if(mode == 2) {
            var source = this.getSource();
            var doc = this.getDocument();
            var iframe = this.getIFrame();
            doc.body.className = "je-source";
            doc.body.innerHTML = "<textarea id=\"je-textarea\"></textarea>";

            var height = iframe.offsetHeight;
            var textarea = this.getTextarea();
            textarea.value = source;
            textarea.style.border = "none";
            textarea.style.outline = "none";
            textarea.style.fontSize = "14px";
            textarea.style.overflow = "hidden";
            textarea.style.width = "100%";
            textarea.style.height = (Math.max(height, textarea.scrollHeight) + 200) + "px";

            EventUtil.addListener(textarea, "keyup", function() {
                com.skin.util.Html.getCursorPosition(doc, textarea);
                textarea.style.height = (textarea.scrollHeight) + "px";
                return true;
            });

            EventUtil.addListener(textarea, "mouseup", function() {
                com.skin.util.Html.getCursorPosition(doc, textarea);
                return true;
            });

            doc.designMode = "Off";
            doc.body.contentEditable = "false";

            this.mode = 2;
            textarea.focus();
            this.getMark().mark(null);
            this.resize();
        }
        else {
            this.mode = 1;
            var doc = this.getDocument();
            doc.designMode = "On";
            doc.body.className = "je-editor";
            doc.body.innerHTML = this.getTextarea().value;
            doc.body.contentEditable = "true";
            this.resize();
        }

        var list = this.getToolbar().childNodes;

        for(var i = 0, length = list.length; i < length; i++) {
            var node = list[i];

            if(node.nodeType != 1) {
                continue;
            }

            var command = node.getAttribute("command");

            if(command != "source") {
                this.setEnabled(node, (this.mode == 1));
            }
        }
    };

    e.setEnabled = function(src, enabled) {
        var node = null;

        if(typeof(src) == "string") {
            node = this.getTool(src);
        }
        else{
            node = src;
        }

        if(node == null) {
            return;
        }

        if(enabled == true) {
            if(this.IE) {
                node.style.opacity = opacity;
                node.style.filter = "alpha(opacity=100)";
            }
            else {
                node.style.opacity = 100;
            }
            StyleUtil.removeClass(node, "disabled");
            StyleUtil.addClass(node, "enabled");
        }
        else {
            if(this.IE) {
                node.style.opacity = opacity;
                node.style.filter = "alpha(opacity=30)";
            }
            else {
                node.style.opacity = 0.3;
            }
            StyleUtil.removeClass(node, "enabled");
            StyleUtil.addClass(node, "disabled");
        }
    };

    e.addStyle = function(url) {
        var doc = this.getDocument();
        var e = doc.createElement("link");
        e.rel = "stylesheet";
        e.type = "text/css";
        e.href = url;
        (doc.getElementsByTagName("head"))[0].appendChild(e);
    };

    e.addScript = function(url) {
        var doc = this.getDocument();
        var e = doc.createElement("script");
        e.type = "text/javascript";
        e.src = url;
        (doc.getElementsByTagName("head"))[0].appendChild(e);
    };

    e.resize = function() {
        var c = this.getContainer();
        var v = this.getIFrame();
        v.style.width = "100%";
        v.style.height = (c.clientHeight - this.getToolbar().clientHeight) + "px";
        v.style.display = "block";
        this.getMark().show();
    };

    e.print = function() {
        this.getWindow().print();
    };
})();

(function() {
    var home = JEditor.getHome();
    var language = (window.navigator.language || navigator.browserLanguage).toLowerCase();

    document.write("<link type=\"text/css\" rel=\"stylesheet\" href=\"" + home + "/css/je.css\"/>");
    document.write("<script type=\"text/javascript\" src=\"" + home + "/lang/" + language + ".js\"></script>");
    document.write("<script type=\"text/javascript\" src=\"" + home + "/je-plugin.js\"></script>");
})();

